Repository navigation
Resolve and load each imported module once per transform - #124
Merged
Merged
Conversation
A transform resolved, loaded, and parsed an imported module again for every value it interpolated from it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars.
|
loadImportedModule now returns the cached promise, and resolveImportedModule does the resolving, loading, and parsing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
nstepien
marked this pull request as ready for review
November 4, 2026 02:00
nstepien
added a commit
that referenced
this pull request
Nov 4, 2026
main reshaped the code this branch builds on since the last merge, so the merge ports the branch onto main's design again: - The scope visitor is shared across parses (#109), so the callbacks for raw TypeScript bindings and `export default` join it, and the default export is recorded through its visit state. - Imported modules are resolved, loaded, and parsed once per transform through main's loadImportedModule (#124), which now also skips external modules, keys modules by moduleKey, and records dependencies by file. - Modules are parsed as the language the bundler assigned them through main's parseLangOf (#125), which this branch had too. - extractCssFromCode keeps main's single pass (#114), and hotUpdate its early return, with this branch's handling of query variants. - readModuleCode moved to module level, as consistent-function-scoping is on since #109. Tests: the inline snapshots of this branch's tests follow Rolldown's new module comments (`// path` instead of `//#region path` and `//#endregion`), with no other change. The dev server test of virtual modules uses main's shared getCode and getStylesheets helpers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Nov 4, 2026
nstepien
added a commit
that referenced
this pull request
Nov 7, 2026
The parse cache only stored finished parses. The transforms of the modules that import the same module run concurrently: in a build, they all wait for `this.load()` of that module and resume together, so each one missed the cache and read and parsed the file again. In a project of 400 components that interpolate values of one `tokens.ts`, it was parsed 400 times per build, and in a cold dev server too. The cache now holds the pending parse of each module read from disk, stored as soon as it is requested, so the file is read and parsed once. The parse of a module being transformed is stored as is. `parseFile` only parses now, and `loadParsedFile` returns the cached or pending parse of a module read from disk, like `loadImportedModule` does for the imported modules of a transform (#124). Caching the promise is safe for the same reason: a parse never waits for a value to resolve, so it can't wait for itself. **Benchmarks** Medians with Vite 8.3.5, #127 against this branch. The JS, CSS, and final source maps are identical. | Workload | #127 | This branch | |---|---|---| | Vite build of a synthetic project: 40 components × 8 templates × 5 tokens from one `tokens.ts` | 28.5 ms | 23.1 ms (−19%) | | The same with 400 components | 224.0 ms | 171.6 ms (−23%) | | Cold dev server transforms of the 40 components | 35.9 ms | 31.8 ms (−11%) | | Cold dev server transforms of the 400 components | 386.1 ms | 339.9 ms (−12%) | | Vite build of react-data-grid's `src`, which reads no module from disk | 20.7 ms | 21.0 ms | `tokens.ts` is now read and parsed once per build and per cold dev server, instead of 400 times. **Other effects** - A read that fails is cached like a parse, until `buildEnd` or `watchChange` clears it. It fails the transform that interpolates the module's values either way. - In the dev server, a read that finishes after `watchChange` forgot the module can no longer store its stale parse, as the pending parse is cached before the read starts. **Alternative** `parse` from `rolldown/utils`, which parses on another thread, was also measured. On its own, it gave up to 11% on these builds, the most where the duplicate parses overlapped, and 0–4% on cold dev transforms. On top of this branch, it gave at most 4%, while Rolldown advises preferring `parseSync`, so it's left out. The tests pass unchanged, with the same uncovered lines and branches as #127. `AGENTS.md` now says why the parse is cached while pending. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
When a template interpolates values imported from another module,
resolveValueresolved, loaded, and parsed that module again for every value, callingcontext.resolveandcontext.loadeach time. A transform now does this once per imported module, however many values it interpolates from it. The modules are keyed by importer and import source, as values can also come through the imports of an imported module.Benchmarks
Medians with Vite 8.3.5, main against this branch. The JS, CSS, and final source maps are identical.
tokens.tssrc, with 9 interpolations in 15 modulesA transform now records each dependency and checks
isWaitingForonce per imported module instead of once per value.The uncovered lines are unchanged: the tests reuse a loaded module 15 times. An import that can't be resolved stays untested, as on main.
#68 includes the same change (
importedFiles), so whichever lands second needs to account for it.AGENTS.mdnow says that a transform handles each imported module once.🤖 Generated with Claude Code